package com.idanchuang.component.gauss.service;

import com.alibaba.fastjson.JSON;
import com.idanchuang.component.base.JsonResult;
import com.idanchuang.component.gauss.config.IdempotentConfig;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

/**
 * @author Richard_yyf
 * @version 1.0 2021/10/27
 */
public class UniqueIdempotentService {

    /**
     * 幂等记录插入成功 - 0
     * 幂等记录插入失败，对应幂等记录状态为进行中 - 1
     * 幂等记录插入失败，对应幂等记录状态为执行成功 - 2
     */
    private static final int INSERT_SUCCESS = 0;
    private static final int IDEMPOTENT_INIT_VALUE = 1;
    private static final int IDEMPOTENT_SUCCESS_VALUE = 2;


    private final IdempotentConfig idempotentConfig;
    private final RestTemplate restTemplate;

    public UniqueIdempotentService(IdempotentConfig idempotentConfig, RestTemplate restTemplate) {
        this.idempotentConfig = idempotentConfig;
        this.restTemplate= restTemplate;
    }

    /**
     * 插入幂等记录
     * @param key 幂等的key
     * @param expireTime 失效时间 单位：s
     * @return 操作结果
     */
    public JsonResult<InsertResult> insert(String key, Long expireTime) {
        IdempotentRequest request = new IdempotentRequest(idempotentConfig.getAppName(), key, expireTime);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(request), headers);
        JsonResult<Integer> result = restTemplate.postForObject(idempotentConfig.getGaussDomain() + "/unique/insert",
                httpEntity, JsonResult.class);

        if (result != null && result.isSuccess()) {
            return JsonResult.success(new InsertResult(result.getData()));
        } else {
            return JsonResult.failure(result);
        }
    }

    /**
     * 更新幂等记录为成功
     * @param key 幂等的key
     * @param expireTime 失效时间 单位：s
     * @return 操作结果
     */
    public JsonResult<Boolean> updateSuccess(String key, Long expireTime) {
        IdempotentRequest request = new IdempotentRequest(idempotentConfig.getAppName(), key, expireTime);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(request), headers);
        return restTemplate.postForObject(idempotentConfig.getGaussDomain() + "/unique/updateSuccess",
                httpEntity, JsonResult.class);
    }

    /**
     * 更新幂等记录为成功
     * @param key 幂等的key
     * @return 操作结果
     */
    public JsonResult<Boolean> delete(String key) {
        IdempotentRequest request = new IdempotentRequest(idempotentConfig.getAppName(), key, null);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(request), headers);
        return restTemplate.postForObject(idempotentConfig.getGaussDomain() + "/unique/delete",
                httpEntity, JsonResult.class);
    }

    static class IdempotentRequest {
        /**
         * 应用标识
         */
        private String appId;
        /**
         * 幂等的key
         */
        private String key;
        /**
         * 失效时间 单位：s
         */
        private Long expire;


        public IdempotentRequest(String appId, String key, Long expire) {
            this.appId = appId;
            this.key = key;
            this.expire = expire;
        }

        public String getAppId() {
            return appId;
        }

        public void setAppId(String appId) {
            this.appId = appId;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public Long getExpire() {
            return expire;
        }

        public void setExpire(Long expire) {
            this.expire = expire;
        }
    }

    public static class InsertResult {

        private final int val;

        private InsertResult(int val) {
            this.val = val;
        }

        public boolean insertSuccess() {
            return val == INSERT_SUCCESS;
        }

        public boolean logicSuccess() {
            return val == IDEMPOTENT_SUCCESS_VALUE;
        }

        public boolean logicProcessing() {
            return val == IDEMPOTENT_INIT_VALUE || val == INSERT_SUCCESS;
        }
    }
}
